package com.denghq.projectbuilder.component.config.spring;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.denghq.projectbuilder.common.util.BeanTool;
import com.denghq.projectbuilder.component.config.metadata.ConfigDescriptor;
import com.denghq.projectbuilder.component.config.metadata.ConfigOptionDescriptor;
import com.denghq.projectbuilder.component.config.metadata.enums.ConfigValueTypeEnum;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;

@Slf4j
public class ConfigPropertySource extends EnumerablePropertySource<String> {
    private String context;

    private final Map<String, Object> properties = new LinkedHashMap();
    private final ConfigDescriptor configDescriptor;

    public ConfigPropertySource(String context, String source, ConfigDescriptor configDescriptor) {
        super(context, source);
        this.context = context;
        this.configDescriptor = configDescriptor;
    }

    public Map<String, Object> getProperties() {
        return this.properties;
    }

    protected String getContext() {
        return this.context;
    }

    public Object getProperty(String name) {
        return this.properties.get(name);
    }

    public String[] getPropertyNames() {
        Set<String> strings = this.properties.keySet();
        return strings.toArray(new String[strings.size()]);
    }

    public void init() {
        try {

            String prefix = configDescriptor.getPrefix();
            ConfigDescriptor remoteConfigDescriptor = BeanTool.jsonStrToObject(source, ConfigDescriptor.class);
            parse(this.properties, prefix, remoteConfigDescriptor);
        } catch (Exception e) {
            log.warn("设置配置【{}】到spring properties出错，{}", source, e.getMessage());
        }

    }

    private void parse(Map<String, Object> properties, String prefix, ConfigDescriptor remoteConfigDescriptor) {

        if (null != remoteConfigDescriptor && null != configDescriptor) {
            final String _prefix = StringUtils.isNotBlank(prefix) ? (prefix + ".") : "";
            List<ConfigOptionDescriptor> localOptions = configDescriptor.getOptionSettings();
            List<ConfigOptionDescriptor> remoteOptions = remoteConfigDescriptor.getOptionSettings();
            if (!CollectionUtils.isEmpty(remoteOptions) && !CollectionUtils.isEmpty(localOptions)) {
                Map<String, Object> values = Maps.newLinkedHashMap();
                //获取所有字段的值
                localOptions.forEach(localOption -> {
                    String configKey = localOption.getConfigKey();
                    for (int i = 0, len = remoteOptions.size(); i < len; i++) {
                        ConfigOptionDescriptor remoteOption = remoteOptions.get(i);
                        if (configKey.equals(remoteOption.getConfigKey())) {
                            values.put(configKey, remoteOption.getConfigValue());
                            break;
                        }
                    }
                });

                //设置所有字段的值到资源属性
                if (!CollectionUtils.isEmpty(values)) {
                    //获取所有字段的值
                    values.forEach((k, v) -> {
                        for (int i = 0, len = localOptions.size(); i < len; i++) {
                            ConfigOptionDescriptor optionDescriptor = localOptions.get(i);
                            String configKey = optionDescriptor.getConfigKey();
                            Field field = optionDescriptor.getField();
                            field.setAccessible(true);
                            if (k.equals(configKey)) {
                                if (null == v) {
                                    this.properties.put(_prefix + field.getName(), null);
                                    break;
                                }
                                if (ConfigValueTypeEnum.DATA_LIST.getValue() == optionDescriptor.getConfigValueType() || ConfigValueTypeEnum.ARRAY.getValue() == optionDescriptor.getConfigValueType()) {

                                    JSONArray jsonArray = JSONObject.parseArray(BeanTool.objectToJsonStr(v));
                                    List<Object> datas = Lists.newArrayList();
                                    //集合
                                    if (Collection.class.isAssignableFrom(field.getType())) {
                                        ParameterizedType parameterizedType = (ParameterizedType) field.getGenericType();
                                        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                                        try {
                                            Class<?> elemClass = this.getClass().getClassLoader().loadClass((actualTypeArguments[0]).getTypeName());
                                            for (Object o : jsonArray) {
                                                if (null != o) {
                                                    datas.add(BeanTool.jsonStrToObject(BeanTool.objectToJsonStr(o), elemClass));
                                                } else {
                                                    datas.add(null);
                                                }
                                            }
                                        } catch (ClassNotFoundException e) {
                                            log.warn("给配置类设置属性【{}】值出错，{}", optionDescriptor.getConfigName(), e.getCause());
                                        }
                                        this.properties.put(_prefix + field.getName(), datas);
                                    }
                                    //java 数组
                                    else {
                                        Class<?> componentType = field.getType().getComponentType();
                                        for (Object o : jsonArray) {
                                            if (null != o) {
                                                datas.add(BeanTool.jsonStrToObject(BeanTool.objectToJsonStr(o), componentType));
                                            } else {
                                                datas.add(null);
                                            }
                                        }
                                        this.properties.put(_prefix + field.getName(), datas.toArray());
                                    }

                                } else {
                                    this.properties.put(_prefix + field.getName(), BeanTool.jsonStrToObject(BeanTool.objectToJsonStr(v), field.getType()));
                                }

                            }
                        }
                    });
                }
            }

        }
    }
}
